package utilities;


/**
 * @author Mathieu Rousseau
 * @email mathieu.rousseau3@mail.mcgill.ca
 */


import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.Date;


/** ------------------------------------------------------------------------------------------------------------------
 * start of PDButility class
 * Version 1.0
 * January 14th, 2011
 * ------------------------------------------------------------------------------------------------------------------  */
public class PDButility {

	
	/*  global variables  */
	private final static String AUTHOR = "ROUSSEAU";
	private final NumberFormat formatterPDB0decimals = new DecimalFormat("0");
	private final NumberFormat formatterPDB1decimals = new DecimalFormat("0.0");
	private final NumberFormat formatterPDB2decimals = new DecimalFormat("0.00");
	private final NumberFormat formatterPDB3decimals = new DecimalFormat("0.000");
	/*  end of global variables  */
	
	
	/*  start of getNumberOfPoints method  */
	public int getNumberOfPoints(String pathFilename){
		String line = null;
		int numberOfPoints = 0;
		try{
			BufferedReader br = new BufferedReader(new FileReader(pathFilename));
			int headerLinesLeft = 2;		//  there are two (2) header lines that need to be skipped
			boolean doneHeader = false;
			boolean doneStructure = false;
			while(((line = br.readLine()) != null) && (!doneStructure)){
				if(doneHeader){
					String[] values = line.split(" ");
					if(values[0].compareTo("ATOM") == 0){
						numberOfPoints++;
					}
					else{
						doneStructure = true;
					}
				}
				else{
//					System.out.println(line);	//  display the header lines
					headerLinesLeft--;
					if(headerLinesLeft == 0){
						doneHeader = true;
					}
				}
			}
			br.close();
		}catch (IOException e) {
			e.printStackTrace();
			System.out.println("Error: cannot find file with filename \"" + pathFilename + "\"\n" +
			"System.exit(0)");
			System.exit(0);
		}
		return numberOfPoints;
	}/*  end of getNumberOfPoints method  */
	
	
	/*  start of writeStructure method  */
	public void writeStructure(String pathFilename, double[][] structure, String header){
		//  make the minimum x-, y-, and z-coordinates of the structure equal to one (1)
		double translationX = Double.MAX_VALUE;
		double translationY = Double.MAX_VALUE;
		double translationZ = Double.MAX_VALUE;
		
		//  find the minimum x,y,z coordinate values
		for(int i=0; i<structure.length; i++){
			if(structure[i][0] < translationX){
				translationX = structure[i][0];
			}
			if(structure[i][1] < translationY){
				translationY = structure[i][1];
			}
			if(structure[i][2] < translationZ){
				translationZ = structure[i][2];
			}
		}
		
		//  subtract one (1.0) to each of the translations to leave the minimum point at coordinate one (1.0) after the translation
		translationX -= 1;
		translationY -= 1;
		translationZ -= 1;
		
		//  translate all of the points in the structure
		for(int i=0; i<structure.length; i++){
			structure[i][0] -= translationX;
			structure[i][1] -= translationY;
			structure[i][2] -= translationZ;
		}
		
		//  write the structure to file
		try{						
			BufferedWriter bw = new BufferedWriter(new FileWriter(pathFilename));
			
			
			//  write the header block
			bw.write("COMPND    " + header.toUpperCase() + "\nAUTHOR    " + AUTHOR + "\n");
			bw.flush();
			

			//  write the structure
			for(int i=0; i<structure.length; i++){
				
				//  write the type of particle
				String lineToWrite = "ATOM  ";
				
				
				//  write the index of the atom
				int atomIndex = i+1;
				if(atomIndex < 10){
					lineToWrite += "    " + atomIndex;
				}
				else if(atomIndex < 100){
					lineToWrite += "   " + atomIndex;
				}
				else if(atomIndex < 1000){
					lineToWrite += "  " + atomIndex;
				}
				else if(atomIndex < 10000){
					lineToWrite += " " + atomIndex;
				}
				else{
					System.out.println("Error: the index value (" + atomIndex + ") is too large to convert to PDB format.\nSystem.exit(0)");
					System.exit(0);
				}
				
				
				//  write the atom used to represent each point
				lineToWrite += " C";
				
				
				//  write the type of atom present
				lineToWrite += "    LIG";
				
				
				//  write the chain identifier
				lineToWrite += " A";
				
				
				//  write the white-space before the coordinates
				//  if all of the coordinates are positive values then this should be nine (9) spaces
				lineToWrite += "         ";
				
								
				//  write the x-coordinate
				//  add the requisite amount of white-space and adjust the number of digits after the decimal (if required)
				if(structure[i][0] < 10){
					lineToWrite += "  ";
					lineToWrite += formatterPDB3decimals.format(structure[i][0]) + " ";
				}
				else if(structure[i][0] < 100){
					lineToWrite += " ";
					lineToWrite += formatterPDB3decimals.format(structure[i][0]) + " ";
				}
				else if(structure[i][0] < 1000){
					//  don't need to add any more white-space
					lineToWrite += formatterPDB3decimals.format(structure[i][0]) + " ";
				}
				else if(structure[i][0] < 10000){
					//  don't need to add any more white-space
					lineToWrite += formatterPDB2decimals.format(structure[i][0]) + " ";
				}
				else if(structure[i][0] < 100000){
					//  don't need to add any more white-space
					lineToWrite += formatterPDB1decimals.format(structure[i][0]) + " ";
				}
				else if(structure[i][0] < 1000000){
					lineToWrite += " ";				//  need to add an extra space to make up for the decimal that is removed
					lineToWrite += formatterPDB0decimals.format(structure[i][0]) + " ";
				}
				else if(structure[i][0] < 10000000){
					//  don't need to add any more white-space
					lineToWrite += formatterPDB0decimals.format(structure[i][0]) + " ";
				}
				else{
					System.out.println("Error: the x-coordinate value is greater than 10000000 and cannot be converted to PDB.\nSystem.exit(0)");
					System.exit(0);
				}
				
				
				//  write the y-coordinate
				//  add the requisite amount of white-space and adjust the number of digits after the decimal (if required)
				if(structure[i][1] < 10){
					lineToWrite += "  ";
					lineToWrite += formatterPDB3decimals.format(structure[i][1]) + " ";
				}
				else if(structure[i][1] < 100){
					lineToWrite += " ";
					lineToWrite += formatterPDB3decimals.format(structure[i][1]) + " ";
				}
				else if(structure[i][1] < 1000){
					//  don't need to add any more white-space
					lineToWrite += formatterPDB3decimals.format(structure[i][1]) + " ";
				}
				else if(structure[i][1] < 10000){
					//  don't need to add any more white-space
					lineToWrite += formatterPDB2decimals.format(structure[i][1]) + " ";
				}
				else if(structure[i][1] < 100000){
					//  don't need to add any more white-space
					lineToWrite += formatterPDB1decimals.format(structure[i][1]) + " ";
				}
				else if(structure[i][1] < 1000000){
					lineToWrite += " ";				//  need to add an extra space to make up for the decimal that is removed
					lineToWrite += formatterPDB0decimals.format(structure[i][1]) + " ";
				}
				else if(structure[i][1] < 10000000){
					//  don't need to add any more white-space
					lineToWrite += formatterPDB0decimals.format(structure[i][1]) + " ";
				}
				else{
					System.out.println("Error: the y-coordinate value is greater than 10000000 and cannot be converted to PDB.\nSystem.exit(0)");
					System.exit(0);
				}
				
				
				//  write the z-coordinate
				//  add the requisite amount of white-space and adjust the number of digits after the decimal (if required)
				if(structure[i][2] < 10){
					lineToWrite += "  ";
					lineToWrite += formatterPDB3decimals.format(structure[i][2]) + " ";
				}
				else if(structure[i][2] < 100){
					lineToWrite += " ";
					lineToWrite += formatterPDB3decimals.format(structure[i][2]) + " ";
				}
				else if(structure[i][2] < 1000){
					//  don't need to add any more white-space
					lineToWrite += formatterPDB3decimals.format(structure[i][2]) + " ";
				}
				else if(structure[i][2] < 10000){
					//  don't need to add any more white-space
					lineToWrite += formatterPDB2decimals.format(structure[i][2]) + " ";
				}
				else if(structure[i][2] < 100000){
					//  don't need to add any more white-space
					lineToWrite += formatterPDB1decimals.format(structure[i][2]) + " ";
				}
				else if(structure[i][2] < 1000000){
					lineToWrite += " ";				//  need to add an extra space to make up for the decimal that is removed
					lineToWrite += formatterPDB0decimals.format(structure[i][2]) + " ";
				}
				else if(structure[i][2] < 10000000){
					//  don't need to add any more white-space
					lineToWrite += formatterPDB0decimals.format(structure[i][2]) + " ";
				}
				else{
					System.out.println("Error: the z-coordinate value is greater than 10000000 and cannot be converted to PDB.\nSystem.exit(0)");
					System.exit(0);
				}
				
				
				//  write the number for the atom
				lineToWrite += " 1.00";
				
				
				//  write the radius for the atom
				lineToWrite += " 75.00";
				
				
				//  write the trailing white-space (if all other formatting conditions have been met then this should be four (4) spaces) 
				lineToWrite += "    ";
				
				
				//  output the line to write to file
				bw.write(lineToWrite);
				bw.newLine();
				bw.flush();
			}
			
			
			//  connect all of the successive points
			for(int i=1; i<structure.length; i++){
				bw.write("CONECT ");
				//  write the first index
				if(i < 10){
					bw.write("   ");
				}
				else if(i < 100){
					bw.write("  ");
				}
				else if(i < 1000){
					bw.write(" ");
				}
				else if(i < 10000){
					//  don't need to add any white-space
				}
				else{
					System.out.println("Error: the first index is greater than 10000, cannot convert to PDB.\nSystem.exit(0)");
					System.exit(0);
				}
				bw.write(String.valueOf(i));
				
				//  write the second index
				int secondIndex = i+1;
				if(secondIndex < 10){
					bw.write("   ");
				}
				else if(secondIndex < 100){
					bw.write("  ");
				}
				else if(secondIndex < 1000){
					bw.write(" ");
				}
				else if(secondIndex < 10000){
					//  don't need to add any white-space
				}
				else{
					System.out.println("Error: the second index is greater than 10000, cannot convert to PDB.\nSystem.exit(0)");
					System.exit(0);
				}
				bw.write(String.valueOf(secondIndex));
				
				bw.newLine();
				bw.flush();
			}
			
			
			//  write the END of the PDB file
			bw.write("END");
			bw.newLine();
			bw.flush();
			
			//  close the writer			
			bw.close();
		}catch (IOException e) {
			System.out.println("Error: could not output file: " + pathFilename);
			e.printStackTrace();
		}//end of try-catch block
	}/*  end of writeStructure method  */

	
	/*  start of getStructure method  */
	public double[][] getStructure(String pathFilename){
		//  declare and initialize the structure (get the number of points from the structure file)
		double[][] structure = new double[getNumberOfPoints(pathFilename)][3];
		
		String line = null;
		int lineCounter = 0;
		try{
			BufferedReader br = new BufferedReader(new FileReader(pathFilename));
			int headerLinesLeft = 2;		//  there are two (2) header lines that need to be skipped
			boolean doneHeader = false;
			boolean doneStructure = false;
			while(((line = br.readLine()) != null) && (!doneStructure)){
				if(doneHeader){
					String[] values = line.split(" ");
					if(values[0].compareTo("ATOM") == 0){
						structure[lineCounter][0] = Double.parseDouble(line.substring(31, 38));
						structure[lineCounter][1] = Double.parseDouble(line.substring(39, 46));
						structure[lineCounter][2] = Double.parseDouble(line.substring(47, 54));
						lineCounter++;
					}
					else{
						doneStructure = true;
					}
				}
				else{
//					System.out.println(line);	//  display the header lines
					headerLinesLeft--;
					if(headerLinesLeft == 0){
						doneHeader = true;
					}
				}
			}
			br.close();
		}catch (IOException e) {
			e.printStackTrace();
			System.out.println("Error: cannot find structure file with filename \"" + pathFilename + "\"\n" +
			"System.exit(0)");
			System.exit(0);
		}
		return structure;
	}/*  end of getStructure method  */
	
	
	/*  start of getHeader method  */
	public String getHeader(String pathFilename){
		String header = null;
		try{
			BufferedReader br = new BufferedReader(new FileReader(pathFilename));
			header = br.readLine();		//  the header is the first line of the pdb file
			String[] values = header.split("    ");		//  remove the leading COMPND
			if(values.length != 2){
				System.out.println("Error reading the header line of " + pathFilename);
				System.out.println("System.exit(0)");
				System.exit(0);
			}
			header = values[1];			
			br.close();
		}catch (IOException e) {
			e.printStackTrace();
			System.out.println("Error: cannot find structure file with filename \"" + pathFilename + "\"\n" +
			"System.exit(0)");
			System.exit(0);
		}
		return header;
	}/*  end of getHeader method  */
}/*  end of PDButility class  */
